﻿using System;
using System.Threading;

namespace WFIM.Framework.Parellel
{
  /// <include file='XMLCodeDocumentation.xml' path='documentation/item[@name="T_ParallelTask"]/*' />
  public class ParallelTask
  {
    //-----------------------------------------------------------------------
    // CONSTANTS
    //-----------------------------------------------------------------------

    //-----------------------------------------------------------------------
    // DELEGATES
    //-----------------------------------------------------------------------
    /// <summary>The callback method that is called when the task has finished execution of the runner.</summary>
    /// <param name="_state">The state object is used to pass parameters and state information to the delegate method.</param>
    public    delegate  void  TaskCallbackDelegate(ParallelState _state);
    /// <summary>The runner method contains the main code which is executed by the task on the threadpool.</summary>
    /// <param name="_state">The state object is used to pass parameters and state information to the delegate method.</param>
    public    delegate  void  TaskRunnerDelegate(ParallelState _state);
    /// <summary>The runner method contains the main code which is executed by the task on the threadpool. This delegate is used by the <see cref="ParallelDispatcher"/>.</summary>
    /// <param name="_state">The state object is used to pass parameters and state information to the delegate method.</param>
    /// <param name="_item">The element from the collection, for which this task was created by the <see cref="ParallelDispatcher"/>.</param>
    public    delegate  void  TaskItemRunnerDelegate(ParallelState _state, object _item);
    internal  delegate  void  TaskParentCallback(ParallelState _state);

    //-----------------------------------------------------------------------
    // FIELDS
    //-----------------------------------------------------------------------
    private ParallelState           state           = null;
    private TaskCallbackDelegate    callback        = null;
    private TaskRunnerDelegate      runner          = null;
    private TaskItemRunnerDelegate  itemRunner      = null;
    private TaskParentCallback      parentCallback  = null;
    private object                  item            = null;

    //-----------------------------------------------------------------------
    // CONSTRUCTORS
    //-----------------------------------------------------------------------
    /// <summary>Constructs an intstance of the ParallelTask class.</summary>
    /// <param name="_runner">The runner method contains the main code which is executed by the task on the threadpool.</param>
    /// <param name="_callback">The callback method that is called when the task has finished execution of the runner.</param>
    /// <param name="_state">The state object is used to pass parameters and state information to the delegate methods.</param>
    public ParallelTask(TaskRunnerDelegate _runner, TaskCallbackDelegate _callback, ParallelState _state)
    {
      state     = _state;
      runner    = _runner;
      callback  = _callback;

      //check for valid delegate
      if (runner == null)
        throw new ArgumentNullException("_runner");
    }

    //-----------------------------------------------------------------------
    /// <summary>Constructs an intstance of the ParallelTask class.</summary>
    /// <param name="_runner">The runner method contains the main code which is executed by the task on the threadpool.</param>
    /// <param name="_state">The state object is used to pass parameters and state information to the delegate methods.</param>
    public ParallelTask(TaskRunnerDelegate _runner, ParallelState _state) : this(_runner, null, _state)
    {}

    //-----------------------------------------------------------------------
    internal ParallelTask(TaskItemRunnerDelegate _runner, ParallelState _state, object _item, TaskParentCallback _parrentCallback, TaskCallbackDelegate _callback)
    {
      state           = _state;
      itemRunner      = _runner;
      callback        = _callback;
      parentCallback  = _parrentCallback;
      item            = _item;

      //check for valid delegate
      if (itemRunner == null)
        throw new ArgumentNullException("_runner");
    }

    //-----------------------------------------------------------------------
    internal ParallelTask(TaskItemRunnerDelegate _runner, ParallelState _state, object _item, TaskParentCallback _parrentCallback) : this(_runner, _state, _item, _parrentCallback, null)
    {}

    //-----------------------------------------------------------------------
    internal ParallelTask(TaskItemRunnerDelegate _runner, ParallelState _state, object _item) : this(_runner, _state, _item, null, null)
    {}

    //-----------------------------------------------------------------------
    // METHODS
    //-----------------------------------------------------------------------
    /// <summary>Executes the runner on the threadpool.</summary>
    /// <returns>Returns <c>true</c> if task was properly started. Returns <c>false</c> when the threadpool is flooded an no memory is available to queue another task.</returns>
    public bool run()
    {
      try
      {
        ThreadPool.QueueUserWorkItem(threadpoolCallback, this);
        return true;
      }
      catch (Exception e)
      {
        return false;
      }
    }

    //-----------------------------------------------------------------------
    private void threadpoolCallback(object _task)
    {
      //get the task at hand
      ParallelTask task = (ParallelTask)_task;

      try
      {
        //run the task's code
        if (runner != null)
          task.runner(task.state);
        else
          task.itemRunner(task.state, task.item);
      }
      catch (Exception excpt)
      {
        //catch any exception during task execution
        task.state.taskException = excpt;
      }

      //do callbacks
      if (task.callback != null)
        task.callback(task.state);
      if (task.parentCallback != null)
        task.parentCallback(task.state);
    }

    //-----------------------------------------------------------------------
    internal void setParentCallback(TaskParentCallback _callback)
    {
      parentCallback = _callback;
    }

    //-----------------------------------------------------------------------
  }
}
